#include "../lib/graph.hpp"
#include <iostream>
#include <fstream>

#include <boost/lexical_cast.hpp>

using namespace std;

Graph::Graph()
{
    currentNew_ = 0;
    source_ = NULL;
    sink_ = NULL;
}

Graph::~Graph()
{
    clear();
}

void Graph::clear()
{
    map<string, Node*>::iterator iter;
    for(iter = nodes_.begin(); iter != nodes_.end(); iter++)
    {
        delete iter->second;
    }
}

Node* Graph::source()
{
    return source_;
}

Node* Graph::sink()
{
    return sink_;
}

void Graph::addNode(Node* n)
{
    nodes_.insert(std::make_pair(n->name_, n));
}

void Graph::setSource(Node* n)
{
    source_ = n;
}

void Graph::setSink(Node* n)
{
    sink_ = n;
}

bool Graph::importGraph(std::string fname)
{
    TiXmlDocument doc(fname);
    if(!doc.LoadFile())
    {
        cout << "Could not import " << fname << endl;
        return false;
    }
    TiXmlNode *root = doc.RootElement();
    TiXmlNode *n = root->FirstChild("node");
    while( n != NULL)
    {
        std::string name;
        if(n->FirstChild("name") == NULL)
        {
            name = "Net" + boost::lexical_cast<string>(currentNew_);
            currentNew_++;
            cout << "Found a node with no name. Renamed to " << name << endl;
        }
        else
        {
            name = n->FirstChild("name")->FirstChild()->Value();
        }
        Node* newNode  = new Node(name);

        for (const TiXmlNode* subn=n->FirstChild(); subn; subn = subn->NextSibling())
        {
            std::string key = subn->Value();
            std::string value = subn->FirstChild()->Value();
            if(key == "name")
                continue;
            newNode->addProperty(key, value);
        }
        if(newNode->name_ == "Source")
            source_ = newNode;
        if(newNode->name_ == "Sink")
            sink_ = newNode;
        addNode(newNode);
        n = root->IterateChildren("node", n);
    }

    TiXmlNode* e = root->FirstChild("edge");
    while(e != NULL)
    {

        if(e->FirstChild("start") == NULL || e->FirstChild("end") == NULL)
        {
            cout << "Malformed edge ignored" << endl;
            e = root->IterateChildren("edge", e);
            continue;
        }
        std::string start = e->FirstChild("start")->FirstChild()->Value();
        std::string end = e->FirstChild("end")->FirstChild()->Value();
        if(nodes_.find(start) == nodes_.end())
        {
            cout << "Could not find start node " << start << endl;
            e = root->IterateChildren("edge", e);
            continue;
        }
        if(nodes_.find(end) == nodes_.end())
        {
            cout << "Could not find end edge " << end << endl;
            e = root->IterateChildren("edge", e);
            continue;
        }
        Node* startNode = nodes_[start];
        Node* endNode = nodes_[end];
        startNode->addChild(endNode);
        endNode->addParrent(startNode);
        e = root->IterateChildren("edge", e);
    }
    TiXmlNode* g = root->FirstChild("global");
    if(g != NULL)
    {
        for (const TiXmlNode* subn=g->FirstChild(); subn; subn = subn->NextSibling())
        {
            std::string key = subn->Value();
            std::string value = subn->FirstChild()->Value();
            properties_.insert(make_pair(key,value));
        }
    }
    return true;
}

bool Graph::exportGraph(string fname)
{
    ofstream out;
    out.open(fname.c_str(), ios::out |ios::trunc);
    if(!out.is_open())
        return false;
    out << "<workflow>" << endl;
    node_it it;
    string edgeText = "";
    for(it = nodes_.begin(); it != nodes_.end(); it++)
    {
        out << "\t<node>" << endl;
        map<string, string> nprop = it->second->properties();
        Node::prop_it jt;
        out << "\t\t<name>" << it->second->name_ << "</name>" << endl;
        for(jt = nprop.begin(); jt != nprop.end(); jt++)
        {
            out << "\t\t<" << jt->first << ">" << jt->second << "</" <<jt->first << ">"<< endl;
        }
        for(size_t i = 0; i< it->second->parrents_.size(); i++)
        {
            edgeText += "\t<edge>\n";
            edgeText += "\t\t<start>" + it->second->parrents_[i]->name_+"</start>\n";
            edgeText += "\t\t<end>" + it->second->name_+"</end>\n";
            edgeText += "\t</edge>\n";
        }
        out << "\t</node>" << endl;
    }
    out << edgeText;

    out << "\t<global>" << endl;
    Node::prop_it graphProp;
    for(graphProp = properties_.begin(); graphProp != properties_.end(); graphProp++)
    {
        out << "\t\t<" << graphProp->first << ">" << graphProp->second << "</" <<graphProp->first << ">"<< endl;
    }
    out << "\t</global>" << endl;

    out << "</workflow>" << endl;

    return true;
}
